package com.rytong.tools.ui;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
import org.keplerproject.luajava.LuaState;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.app.Activity;
import android.text.TextUtils;
import com.rytong.tools.crypto.AESCipher;
import com.rytong.tools.httpconnect.HttpManager;
import com.rytong.tools.parserwidgets.ParseWidgets;
import com.rytong.tools.utils.Utils;

public class AtomParser extends DefaultHandler {
    // br节点高度，如果有连续的br节点，则存在br节点高度值，且该值项目组可通过brHeight配置具体大小
    protected int brHeight_;
    // 判断br节点是否连续
    protected int brIndex_;
    // 脚本文件,该内容应该在解析完页面后执行,
    private Stack scriptStack_;
    // 节点样式display属性列表，存储每个节点样式display的属性值。由于在节点尾需要读取对应的display属性，所以创建该堆栈
    private Stack displayStack_;
    // 标志控件在Br节点之后
    protected static boolean ISAFTERBR;
    // 是否已插入界面中部结构
    boolean isInsertLLV_;
    protected Activity activity_;
    // 节点队列，方便添加子节点
    Stack stack_;
    // 节点名堆栈
    Stack nodeNameStack_;
    final int START_DOCUMENT = 0;
    final int END_DOCUMENT = 1;
    final int START_TAG = 2;
    final int END_TAG = 3;
    final int TEXT = 4;
    // 触发的事件类型
    int eventType_;
    // 读取中间节点值的次数
    int readTextIndex_;
    // 存储节点值
    StringBuffer buf_ = new StringBuffer(10);
    private LPPieChart pieChart_;
    private LPSelect select_;
    // 解析类型，是全部解析或者局部解析
    protected int parseType_;
    // 解析出来是整个界面结构标志
    public static final int ENTIRE = 0;
    // 解析出来是局部控件标志
    public static final int PARTLY = 1;
    // 存储整个界面的样式,且里面内容只为当前界面样式一个元素,该界面指整个手机界面
    // 并非弹出框的显示的部分界面,与PARTLY相对应
    protected static Stack ENTIRESTACK;
    // 报文是否含样式节点标记
    protected boolean haveStyle_;
    /**
     * 含有position:fixed属性的控件。
     */
    private boolean isFixed_ = false;
    /**
     * body控件。
    */
    private ComponentCopy body_ = null;
    // 样式源字符串。
    private String CssContent_ = "";

    /**
     * 
     * @param activity
     * @param type
     *            解析类型 0 整个界面结构解析 1 局部控件解析
     */
    public AtomParser(Activity activity, int type) {
        activity_ = activity;
        parseType_ = type;
    }

    // XML开始解析时调用此方法
    public void startDocument() throws SAXException {
    	Component.hasCssStyle = false;
    	
        eventType_ = START_DOCUMENT;
        stack_ = new Stack();
        nodeNameStack_ = new Stack();
        displayStack_ = new Stack();
        scriptStack_ = new Stack();
        if (parseType_ == ENTIRE) {
        	// 刚刚开始解析新的报文，不需要释放资源，等报文解析成功，替换页面内容的时候再进行资源的释放。
//            Utils.clearWidget();
            Component.VWIDGETARRAY = new ArrayList(2);
            ENTIRESTACK = new Stack();
            Component.radioList_ = null;
        }
        ComponentCopy.CC = null;
    }

    // 解析到Element的开头时调用此方法
    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
    	eventType_ = START_TAG;
        // 将读取节点值次数标记复位
        readTextIndex_ = 0;
        nodeNameStack_.add(localName);
        if(localName.equalsIgnoreCase("link")){
        	    //<link ref="css/base.css,style.css" type="text/css"/>
        	    String ref = getAttrString(atts, "ref");
        	    String type = getAttrString(atts, "type");
        	    if(!TextUtils.isEmpty(ref) && !TextUtils.isEmpty(type) && type.equalsIgnoreCase("text/css")){
        	        String[] names = ref.split(",");//多个文件用 ',' 分割
        	        
        	        for(int i = 0 ; i < names.length ; i++){
        	            
        	            
        	            if(!names[i].equals("")){
        	            	String temp = Utils.readLocalFileForStr(names[i]);
        	            	if(!TextUtils.isEmpty(temp)){
        	            	    CssContent_  += temp ;
        	                }
        	            }
        	        }
        	    }
        	} else if (localName.equalsIgnoreCase("script")) {
//            Component.LUASTATE.gc(LuaState.LUA_GCCOLLECT, 0);
            String src = getAttrString(atts, "src");
            if (null != src) {
            	//如果模板开发人员不写版本号，默认调用当前最高版本
            	if(src.equalsIgnoreCase("RYTL.lua")){
            	    String luaVersion = Utils.getConfigStringFormAsset(activity_, "LuaMaxVersion");
            	    if (!TextUtils.isEmpty(luaVersion)) {
            	        src = luaVersion;                	
            	    }
//                    loadLocalLuaFile(src);
                }
            	//从本地资源文件加载
            	String luaTemp = loadLocalLuaFile(src);
            	if(TextUtils.isEmpty(luaTemp)){
            	    //本地找不到时，从服务器上下载
            	    luaTemp = downloadLuaFromServer(src);
            	}
            	if(!TextUtils.isEmpty(luaTemp)){
            	    scriptStack_.add(luaTemp);
            	} 
            }
        } else if (((nodeNameStack_.indexOf("body") != -1) && (parseType_ == ENTIRE)) || (parseType_ == PARTLY)) {
            if (localName.equalsIgnoreCase("style") || localName.equalsIgnoreCase("head")
                    || localName.equalsIgnoreCase("content"))
                return;
            ComponentCopy ccCopy = new ComponentCopy();
            ccCopy.setName(localName);
            final int size = atts.getLength();
            for (int i = 0; i < size; i++) {
                // 循环读取节点中的属性，存入结构
                ccCopy.addAttrs(atts.getLocalName(i), Utils.insteadOfSpecillCharacter(atts.getValue(i).trim(), false));
            }

            if (!stack_.isEmpty()) {
                String temp = null;
                if (null != ccCopy.nodeAttrsMap_ && !ccCopy.nodeAttrsMap_.isEmpty()) {
                    // 取当前节点的属性值,如果存在的话
                    temp = (String) ccCopy.nodeAttrsMap_.get("class");
                }
                if (null != temp && null != CssStyle.STYLEHASH) {
                    CssStyle cs = (CssStyle) CssStyle.STYLEHASH.get(temp.trim());
                    if (null != cs && null != cs.position_ ) {
                    	// 解析到了fixed控件。
                    	isFixed_ = true;
                    }
                }
            }

            String className = atts.getValue("class");
            String display = null;
            if (null != className) {
                if (null != CssStyle.STYLEHASH) {
                    CssStyle cs = (CssStyle) CssStyle.STYLEHASH.get(className.trim());
                    if (null != cs) {
                        display = (String) cs.getProperty(CssStyle.DISPLAY);
                    }
                }
            }
            displayStack_.add(display);
            if (!localName.equalsIgnoreCase("body") && (!stack_.isEmpty())) {
            	// 如果解到了fixed控件，而且还没有添加llv，则是第一个fixed控件，直接加入body后面。其实第一个控件肯定是fixed。
            	if(isFixed_ == true && isInsertLLV_ == false){
            		body_ = (ComponentCopy) stack_.peek();
            		body_.addChilds(ccCopy);
            	}
            	// 如果是普通控件，但是llv还没有添加上，那么这些控件是属于顶部title区域的。
            	else if(isFixed_ == false && isInsertLLV_ == false){
            		((ComponentCopy) stack_.peek()).addChilds(ccCopy);
            	}
            	// 如果是llv中的普通控件，直接加入llv中。
            	else if(isFixed_ == false && isInsertLLV_ == true){
            		((ComponentCopy) stack_.peek()).addChilds(ccCopy);
            	}
            	// 如果遇到了fixed控件，但是llv已经添加，那么fixed控件直接加入body。
            	else if(isFixed_ == true && isInsertLLV_ == true){
            		body_.addChilds(ccCopy);
            	}
            }
            stack_.add(ccCopy); // 这里如果第一次解析到body节点，将会把body控件压入statck。
        }
        isFixed_ = false;
    }

    /**
     * 加载本地lua文件;读取文件的优先级顺序为：插件资源 > 离线下载资源 > file：write写出的资源 > 安装包资源。
     * 
     * @param fileName
     */
    private String loadLocalLuaFile(String fileName) {
    	String lua = Utils.readLocalFileForStr(fileName);
//    	if(!TextUtils.isEmpty(lua)){
//    	    scriptStack_.add(lua);
//    	}
    	return lua;
    }

    /**
     * 根据src中的url去服务器download指定的lua代码并加载到lua引擎中
     * 
     * @param src
     *            test_s/get_page接口，并传递 name = channels/FILEPATH的参数
     */
    private String downloadLuaFromServer(String src) {
    	String reply = ""; 
    	if(!TextUtils.isEmpty(src)){
    	    try{
    	        String resInter = Utils.getConfigStringFormAsset(activity_, "GET_RESFILE_URI");
    	        String uri = Utils.getConfigStringFormAsset(activity_, "SERVER_URI").concat(resInter);
    	        if(!src.startsWith("/")){
    	            src = "/" + src;
    	        }
    	        String body = Utils.getConfigStringFormAsset(activity_, "GET_RESFILE_BODYPARAM")  + src;
    	        Utils.printOutToConsole("downloadLuaFromServer()uri------" + uri);
    	        body = AESCipher.encrypt(body, AESCipher.clientKey_, AESCipher.clientIv_);
    	        Utils.printOutToConsole("downloadLuaFromServer()body------" + body);
    	        HttpManager hm = new HttpManager(activity_);
    	        Object obj = hm.sendPostRequest(uri, body, null, null);
    	        if(obj instanceof byte[]){
    	            reply = new String((byte[])obj , "UTF-8");
    	        }
    	        reply = AESCipher.decrypt(reply, AESCipher.serverKey_, AESCipher.serverIv_);
    	        reply = Utils.unescapeHTML(reply);
    	        reply = Utils.insteadOfSpecillCharacter(reply, true);
    	        //<?xml version='1.0' encoding='utf-8'?> <error string="invalid file path"/>
    	        if(reply.indexOf("<error") != -1){
    	            //返回错误信息
    	            reply = "";
    	        }
    	    }catch(Exception e){
    	        Utils.printException(e);
    	    }
        }
    	return reply;
    }

    // XML解析结束时调用此方法
    public void endDocument() throws SAXException {
        eventType_ = END_DOCUMENT;
        activity_.runOnUiThread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                // 当xml元素转为view的时候，防止view在创建过程中其他线程对view进行操作导致的异常

                xmlToView(ComponentCopy.CC, null);

                if (parseType_ == ENTIRE) {
                    if (null != ComponentCopy.COMPONENT)
                        ComponentCopy.COMPONENT.mouldAndLayout();
                } else {
                    if (null != ComponentCopy.COMPONENTPARTLY)
                        ComponentCopy.COMPONENTPARTLY.mouldAndLayout();
                }
                // 加载新的Lua脚本之前先清除一下已经绑定的物理按键。
                /* 清除存储手机物理按键监听事件集合 */
        		if (Utils.keyHash_ != null) {
        			Utils.keyHash_.clear();
        			Utils.keyHash_ = null;
        		} // 之后如果有绑定物理按键的Lua脚本就加载新的。
        		// 清空同步请求的个数。
        		Utils.SYN_REQUEST_NUMBER_ = 0;
        		Utils.SYN_SUBMIT_REQUEST_NUMBER_ = 0;
                if (null != scriptStack_ && scriptStack_.size() > 0) {
                    final int size = scriptStack_.size();
                    String scriptText = "";
                    int index;
                    synchronized (this) {
                        for (int i = 0; i < size; i++) {
                            scriptText = scriptStack_.get(i).toString();
                            index = Component.LUASTATE.LdoString(scriptText);

                            if (index > 0) {
                                Utils.printOutToConsole("Lua Text has some errors:");
                                Utils.printOutToConsole(Component.LUASTATE.LcheckString(-1));
                            }
                        }
                    }
                    Component.LUASTATE.gc(LuaState.LUA_GCCOLLECT, 0);
                }
                //控件解析完成后，重新做一下控件联动 add by cheng.l
                Utils.setCompontents();
                // 清空该类用到的相关资源
                body_ = null;
                stack_ = null;
                nodeNameStack_ = null;
                displayStack_ = null;
                pieChart_ = null;
                select_ = null;
                activity_ = null;
                buf_ = null;
                if (null != scriptStack_)
                    scriptStack_.removeAllElements();
                scriptStack_ = null;

            }
        });

    }

    /**
     * 该方法将控件从树状结构ComponentCopy中解析出来,并将他们逐个加到上一级的childrenList_队列中
     * 
     * @param cc
     *            为控件自己
     * @param component
     *            为控件父控件
     */
    public void xmlToView(ComponentCopy cc, Component component) {
        // TODO Auto-generated method stub
        try {
            if (null == cc)
                return;
            // 遍历cc中的所有元素，如果是样式报文，则将样式报文分解成样式序列
            final String name = cc.nodeName_;
            String nameType = (String) cc.getAttr("type");
            if (null == nameType)
                nameType = "";
            // 如果cc是其他元素则查看它是不是控件
            // 先将cc在控件列表ParseWidgets.WIDGETSLIST中对应的控件查找出来

            Object o = ParseWidgets.initWidget(cc.nodeName_.concat("-").concat(nameType));
            if (o instanceof LPWrap) {
                ISAFTERBR = true;
                brIndex_++;
                // br节点连续，则需要计算控件前面br的累加高度和框架内控件间距，
                // br高度值在Config配置文件中通过brHeight设置
                if (brIndex_ > 1) {
                    int h = 0;
                    if (component instanceof LPLayout)
                        h = ((LPLayout) component).getVG();
                    brHeight_ += Utils.getBrHeight() + h;
                }
                return;
            } else {
                brIndex_ = 0;
            }

            if (null != o) {
                Component comp = (Component) o;
                // 如果元素o不为空，说明是系统控件此时要做以下几点事情:
                // 1、将对应的cssstyle样式赋给该控件
                // 如果当前报文没有style样式节点，且解析类型为局部解析
                // 表示当前报文做的操作为局部刷新，控件样式需要对应到最近的全局报文中去
                if (!haveStyle_ && parseType_ == PARTLY && null != ENTIRESTACK && !ENTIRESTACK.isEmpty())
                    CssStyle.STYLEHASH = (HashMap) ENTIRESTACK.get(0);
//                comp.setCssStyle((CssStyle) CssStyle.STYLEHASH.get(cc.nodeAttrsMap_.get("class")));
                // change by zhang.jie 
                if(CssStyle.STYLEHASH != null ){
                    comp.setCssStyle((CssStyle) CssStyle.STYLEHASH.get(cc.nodeAttrsMap_.get("class")));
                }
                // 2、将对应的属性集合赋给该控件
                comp.setProperty((HashMap) cc.getAttrs());
                if (ISAFTERBR) {
                    // 赋值换行标记
                    comp.isAfterBr_ = ISAFTERBR;
                    ISAFTERBR = false;
                }

                comp.setBrHeight(brHeight_);
                // br节点不连续，即使将br节点高度值清零
                brHeight_ = 0;
                // 所有控件的显示值存储在节点值中
                comp.property_.put("text", cc.nodeValue_);
                comp.initRealView(activity_, cc.nodeValue_);
                comp.childrenList_ = new ArrayList<Component>(2);
                comp.setPropertyByName("tagname", cc.nodeName_);
                comp.setTag(cc.nodeName_);
                // 将控件加入控件集合
                Component.VWIDGETARRAY.add(comp);
                if (null != component) {
                    component.childrenList_.add(comp);
                    comp.setParent(component);
                } else {
                    if (parseType_ == ENTIRE) {
                        ComponentCopy.COMPONENT = comp;
                    } else {
                        ComponentCopy.COMPONENTPARTLY = comp;
                    }
                }

                // 4、检查该控件是否存在子元素，如果是则遍历其子元素
                int size = cc.getChildren().size();
                for (int i = 0; i < size; i++) {
                    xmlToView(cc.getChild(i), comp);
                }

            }
        } catch (Exception e) {
            Utils.printException(e);
        }
    }

    private void setClassField(Class c, String name, Object value) {
        // TODO Auto-generated method stub
        try {
            Field field = null;
            field = c.getDeclaredField(name);
            field.set(field, value);
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        }
    }

    // 解析到Element的结尾时调用此方法
    public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
    	eventType_ = END_TAG;
        String content = "";
        if (null != buf_) {
            content = buf_.toString().trim();
            content = Utils.insteadOfSpecillCharacter(content, false);
        }
        if (localName.equalsIgnoreCase("style")) {
            haveStyle_ = true;
            // 读取界面的样式值，并赋给stylehash变量
            content = content + CssContent_;
            CssStyle.parseStyle(content);
            // 存储全局界面的样式，该栈元素始终唯一，保存当前全界面的样式
            if (parseType_ == ENTIRE)
                ENTIRESTACK.add(CssStyle.STYLEHASH);
            // 清空节点值
            buf_ = null;
            return;
        } else if (localName.equalsIgnoreCase("script")) {
            if (null != content && !content.equals("")) {
                // Component.LUASTATE.LdoString(content);
                scriptStack_.add(content);
            }
            // 清空节点值
            buf_ = null;
            return;
        }
        if ((nodeNameStack_.indexOf("body") != -1) && (parseType_ == ENTIRE)) {
            // 将该元素赋给静态变量，以备程序调用
            if (!stack_.isEmpty() && null == ComponentCopy.CC)
                ComponentCopy.CC = (ComponentCopy) stack_.get(0);
            ComponentCopy cc = (ComponentCopy) stack_.peek();
            cc.setValue(content);
            displayStack_.pop();
            // 将该节点出栈，使栈顶元素为其上层节点，方便其上层节点添加新的子元素
            stack_.pop();
            if (null != CssStyle.STYLEHASH) {
                CssStyle cs = (CssStyle) CssStyle.STYLEHASH.get(cc.nodeAttrsMap_.get("class"));
                insertLLVCode(cs);
            }
        } else if (parseType_ == PARTLY) {
            if (localName.equalsIgnoreCase("head") || localName.equalsIgnoreCase("content"))
                return;
            ComponentCopy cc = null;
            if (!stack_.isEmpty()) {
                cc = (ComponentCopy) stack_.peek();
                cc.setValue(content);
                if (null == ComponentCopy.CC)
                    ComponentCopy.CC = (ComponentCopy) stack_.get(0);
            }
            displayStack_.pop();
            // 将该节点出栈，使栈顶元素为其上层节点，方便其上层节点添加新的子元素
            stack_.pop();
            if (null != CssStyle.STYLEHASH && cc != null) {
            	CssStyle cs = (CssStyle) CssStyle.STYLEHASH.get(cc.nodeAttrsMap_.get("class"));
            	insertLLVCode(cs);
            }
        }
        nodeNameStack_.pop();
        // 清空节点值
        buf_ = null;
    }

    /**
     * 插入llV节点,该节点是android界面结构需要,手动加入,在服务端提供的xml报文中不会有体现
     * 
     * @param cs
     */
    private void insertLLVCode(CssStyle cs) {
        // TODO Auto-generated method stub
        // 取得控件样式position对应的值,决定是否插入llv节点,该控件纯粹是为了管理界面结构,没有任何表现形式.
        if (null != cs && null != cs.position_) {
            // 获取控件的position样式
            if (!isInsertLLV_) {
                // 更改标记，表示已执行插入操作
                isInsertLLV_ = true;

                // 在llv节点之前插入br节点，模拟换行效果
                ComponentCopy tempBr = new ComponentCopy();
                tempBr.setName("br");
                if (!stack_.isEmpty())
                    ((ComponentCopy) stack_.peek()).addChilds(tempBr); // 在body后面加了一个换行
                
                // 尚未插入界面中部总管理器节点，在此执行插入操作
                ComponentCopy temp = new ComponentCopy();
                temp.setName("llv");
                if (!stack_.isEmpty()) {
                    ((ComponentCopy) stack_.peek()).addChilds(temp);
                    stack_.add(temp); // 如果是第一次解析到顶部的position控件，将会在body下插入一个llv。
                }
            }
        }
    }

    // 取得Element的中间夹的字符串
    public void characters(char ch[], int start, int length) {
        if (eventType_ == END_TAG && length > 1) {
            // 如果紧接着end_tag后面有需要获取出来的数据则执行
            readTextIndex_ = 0;
        }
        try {
            eventType_ = TEXT;
            if (readTextIndex_ == 0) {
                // 节点计数器为零表示是第一次读取该节点值。应将以前读取的值全部清空。该处执行赋空操作，如果执行delete操作数据量大的话会引起内存溢出问题。
                buf_ = null;
                buf_ = new StringBuffer(10);
            }
            // 读取节点值
            if (null != buf_)
                buf_.append(ch, start, length);
            // 节点值计数器累加
            readTextIndex_++;

        } catch (Exception e) {
            // TODO Auto-generated catch block
            Utils.printException(e);
        }
    }

    /**
     * @param atts
     * @param attrName
     * @return
     */
    private final String getAttrString(Attributes atts, String attrName) {
        String attr = null;
        try {
            attr = Utils.insteadOfSpecillCharacter(atts.getValue(attrName), false);
        } catch (Exception e) {
            Utils.printException(e);
        }
        return attr;
    }
}
